package com.yanqu.backendweb.intercept;

import com.alibaba.fastjson.JSONObject;
import com.yanqu.backendweb.exception.BusinessException;
import com.yanqu.backendweb.utils.Response;
import com.yanqu.backendweb.utils.eErrorCode;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.multipart.support.MissingServletRequestPartException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolationException;

/**
 * @author duli
 * @version 1.0
 * @date 2020/22/01 11:44
 * @Description 统一的异常处理出口
 */

@ControllerAdvice
@ResponseBody
public class ExceptionIntercept {
    private final static Logger log = LoggerFactory.getLogger(ExceptionIntercept.class);
    @ExceptionHandler(BusinessException.class)
    public Object businessExceptionHandler(HttpServletRequest req, BusinessException e) {
        String message = e.getMessage();
        if (StringUtils.isBlank(message)) {
            message = eErrorCode.getMsg(e.getCode());
        }

        if (log.isInfoEnabled()) {
            log.warn(String.format("\n业务异常：%d - \"%s\"，请求地址: %s - %s，请求参数: %s", e.getCode(),
                    message, req.getMethod(), req.getRequestURI(), getParameters(req)), e);
        }

        return new Response<>(e.getCode(), message);
    }

    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public Object servletExceptionHandler(HttpServletRequest req, ServletException e) {
        return new Response<>(eErrorCode.REQUEST_METHOD_NOT_ALLOWED.getCode(), "请求方法错误：" + req.getMethod());
    }


    @ExceptionHandler({MethodArgumentNotValidException.class, BindException.class,
            ConstraintViolationException.class, MissingServletRequestPartException.class})
    public Object validationExceptionHandler(HttpServletRequest req, Exception e) {
        StringBuilder sb = new StringBuilder();
        if (e instanceof ConstraintViolationException || e instanceof MissingServletRequestPartException) {
            sb.append(e.getMessage());
        } else {
            BindingResult bindingResult = null;
            if (e instanceof MethodArgumentNotValidException) {
                bindingResult = ((MethodArgumentNotValidException) e).getBindingResult();
            } else if (e instanceof BindException) {
                bindingResult = ((BindException) e).getBindingResult();
            }
            if (bindingResult != null) {
                for (ObjectError error : bindingResult.getAllErrors()) {
                    if (error instanceof FieldError) {
                        sb.append(((FieldError)error).getField());
                        sb.append(error.getDefaultMessage());
                        sb.append(";");
                    }
                }
            }
        }
        if (log.isInfoEnabled()) {
            log.info("\n表单验证错误，请求地址: {} - {}，信息：{} - \"{}\"", req.getMethod(), req.getRequestURI(),
                    e.getClass().getSimpleName(), sb.toString());
        }
        return new Response<>(eErrorCode.FORM_ERROR.getCode(), sb.toString());
    }

    @ExceptionHandler({
            MethodArgumentTypeMismatchException.class,
            HttpMessageNotReadableException.class,
            HttpMediaTypeNotSupportedException.class,
            MissingServletRequestParameterException.class // GET 请求
    })
    public Object methodArgumentTypeMismatchExceptionHandler(HttpServletRequest req, Exception e) {
        if (log.isWarnEnabled()) {
            log.warn("\n参数转换错误，请求地址：{} - {}，请求参数：{}，原因：{}", req.getMethod(), req.getRequestURI(),
                    getParameters(req), e.getMessage());
        }
        return new Response<>(eErrorCode.METHOD_ARGUMENT_ERROR);
    }

    private String getParameters(HttpServletRequest req) {
        // 此拦截器不支持获取 POST 的参数
        JSONObject jsonObject = (JSONObject) JSONObject.toJSON(req.getParameterMap());
        return jsonObject.toJSONString();
    }


}
